# -*- coding: utf-8 -*-
import os
import time
import traceback
from datetime import date
from time import sleep

from selenium.webdriver import ActionChains
import allure
from selenium.webdriver.support import expected_conditions as EC
from common import log
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By


class Page:
    driver = None
    logger = log.logger

    # 构造方法，用来接收selenium的driver对象
    @classmethod
    def __init__(cls, driver):
        cls.driver = driver

    # 输入地址
    @classmethod
    def get(cls, url):
        cls.driver.get(url)

    # 关闭浏览器驱动
    @classmethod
    def quit(cls):
        cls.driver.quit()

    # 删除cookies退出账号再刷新界面
    @classmethod
    def page_logout(cls):
        cls.driver.delete_all_cookies()
        cls.driver.refresh()

    # 返回上一页
    @classmethod
    def back_page(cls):
        cls.driver.back()

    # 刷新页面
    @classmethod
    def page_refresh(cls):
        cls.driver.refresh()

    # 获取界面标题
    @classmethod
    def page_title(cls):
        page_title = cls.driver.title
        return page_title

    # 获取当前url
    @classmethod
    def page_url(cls):
        page_url = cls.driver.current_url
        return page_url

    # element对象
    @classmethod
    def element(cls, module, page, element):
        """
        :param module:
        :param page:
        :param element:
        :return:
        """
        try:
            if cls.page_is_loading():
                # 元素默认是隐藏的，导致等待过早的就结束了，导致报错了
                # WebDriverWait(cls.driver, 10).until(EC.presence_of_element_located(*module[page][element]))

                # el = WebDriverWait(cls.driver, 10).until(EC.visibility_of(cls.driver.find_element(*module[page][element])))
                # el = WebDriverWait(cls.driver, 10).until(EC.presence_of_element_located((module[page][element][0],module[page][element][1])))
                el = WebDriverWait(cls.driver, 10).until(
                    EC.visibility_of_element_located((module[page][element][0], module[page][element][1])))
                # el = cls.driver.find_element(*module[page][element])
                # 判断是否可见，感觉没必要
                # A = EC.visibility_of_element_located(module[page][element][1])
                # print(f"A:{A}")
                # print(f"el:{el}")
                return el
        except:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'查找元素：{element}--fail', extra=m)
            return None

    # elements对象
    @classmethod
    def elements(cls, module, page, element):
        try:
            if cls.page_is_loading():
                # els = WebDriverWait(cls.driver, 10).until(EC.presence_of_all_elements_located(module[page][element]))
                els = WebDriverWait(cls.driver, 10).until(
                    EC.visibility_of_element_located((module[page][element][0], module[page][element][1])))
                return els
        except:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'查找元素：{element}--fail', extra=m)
            return None

    # @classmethod
    # def el_xpath(cls, msg, time=10):
    #     try:
    #         if cls.page_is_loading():
    #             # el = WebDriverWait(cls.driver, time).until(lambda driver: driver.find_element_by_xpath(msg))
    #             el = WebDriverWait(cls.driver, 10).until(EC.visibility_of(cls.driver.find_element_by_xpath(msg)))
    #             return el
    #     except:
    #         s = traceback.extract_stack()
    #         m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    #         cls.logger.info(f'查找元素：{msg}--fail', extra=m)
    #         return None
    #
    # @classmethod
    # def els_xpath(cls, msg, time=10):
    #     try:
    #         if cls.page_is_loading():
    #             # els = WebDriverWait(cls.driver, time).until(lambda driver: driver.find_elements_by_xpath(msg))
    #             els = WebDriverWait(cls.driver, 10).until(EC.visibility_of(cls.driver.find_elements_by_xpath(msg)))
    #             return els
    #     except:
    #         s = traceback.extract_stack()
    #         m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    #         cls.logger.info(f'查找元素：{msg}--fail', extra=m)
    #         return None

    # 判断页面元素是否可见，默认等待5秒（通用方法）
    @classmethod
    def element_is_exist(cls, module, page, element):
        """
        当我们需要找到元素，并且判断该元素是否不可见时用invisibility_of_element_located或者invisibility_of_element
        """
        try:
            WebDriverWait(cls.driver, 5).until_not(EC.invisibility_of_element(module[page][element]))
            return True
        except:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info('页面存在元素：' + element, extra=m)
            return False

    # 判断页面是否在加载中，默认等待10秒
    @classmethod
    def page_is_loading(cls):
        try:
            WebDriverWait(cls.driver, 10).until(EC.invisibility_of_element((By.XPATH, '//*[contains(text(),"加载中")]')))
            return True
        except:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info('页面加载超时。', extra=m)
            return False

    # 等待某元素的文字发生变化

    # sendKey方法
    @classmethod
    def send_key(cls, module, page, element, msg, is_els=False, num=None, log_info=None):
        i = 0
        s = traceback.extract_stack()
        m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
        # print(m)
        while i < 3:
            if not is_els:
                el = cls.element(module, page, element)
            else:
                el = cls.elements(module, page, element)[num]
            i = i + 1
            try:
                el.clear()
                el.send_keys(msg)
                cls.logger.info(f'输入成功--{element}:{msg}', extra=m)
                if log_info:
                    cls.logger.info(f'{log_info}', extra=m)
                break
            except Exception as e:
                cls.logger.info(f'{element}：第{i}次输入失败,错误原因：{e}', extra=m)
                if i == 3:
                    with allure.step('添加失败截图...'):
                        allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # click方法
    @classmethod
    def click(cls, module, page, element, is_els=False, num=None, log_info=None):
        """
        :param log_info: 日志信息
        :param module:
        :param page:
        :param element:
        :param is_els: 判断是否是元素list
        :param num: 元素list下标
        :return:
        """
        i = 0
        el = None
        while i < 3:
            if not is_els:
                el = cls.element(module, page, element)
            if is_els:
                el = cls.elements(module, page, element)[num]
            try:
                i = i + 1
                # Page.page_is_loading()
                el.click()
                s = traceback.extract_stack()
                m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
                cls.logger.info(f'点击成功--{element}', extra=m)
                if log_info:
                    cls.logger.info(f'{log_info}', extra=m)
                    # 当页面跳转比js执行快时，给element加样式则会失败
                try:
                    cls.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", el,
                                              "background: #1AFD9C; border:2px solid red;")
                except:
                    pass
                break
            except Exception as e:
                sleep(0.1)
                s = traceback.extract_stack()
                m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
                cls.logger.info(f'{element}:第{i}次点击失败,错误原因：{e}', extra=m)
                # 连续失败3次，跳出循环了，再截图，完美
                if i == 3:
                    with allure.step('添加失败截图...'):
                        allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # 悬停鼠标
    @classmethod
    def move_to_element(cls, el_module, el_page, el_element, log_info=None):
        try:
            Page.page_is_loading()
            el = cls.element(el_module, el_page, el_element)
            action = ActionChains(cls.driver)
            action.move_to_element(el)
            action.perform()
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'鼠标悬停在元素:{el_element}', extra=m)
            if log_info:
                cls.logger.info(f'{log_info}', extra=m)
        except Exception as e:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'鼠标悬停在元素:{el_element}失败,错误原因：{e}', extra=m)
            with allure.step('添加失败截图...'):
                allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    @classmethod
    def move_to_element_and_click_element(cls, ela_module, ela_page, ela_element, elb_module, elb_page, elb_element, log_info=None):
        try:
            Page.page_is_loading()
            el_a = cls.element(ela_module, ela_page, ela_element)
            el_b = cls.element(elb_module, elb_page, elb_element)
            action = ActionChains(cls.driver)
            action.move_to_element(el_a)
            action.click(el_b)
            action.perform()
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'鼠标悬停在元素:{ela_element},并点击元素{elb_element}', extra=m)
            if log_info:
                cls.logger.info(f'{log_info}', extra=m)
        except Exception as e:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'鼠标悬停在元素:{ela_element}，并点击元素{elb_element}失败,错误原因：{e}', extra=m)
            with allure.step('添加失败截图...'):
                allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # 获取元素的text属性,还有点问题，一直获取不到text文本
    @classmethod
    def element_text(cls, el_module, el_page, el_element, method='textContent', log_info=None):
        try:
            Page.page_is_loading()
            el_text = str(cls.element(el_module, el_page, el_element).get_attribute(method))
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'{el_element}的text文本为：{el_text}', extra=m)
            if log_info:
                cls.logger.info(f'{log_info}', extra=m)
            return str(el_text)
        except Exception as e:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'{el_element}获取文本失败,错误原因：{e}', extra=m)
            with allure.step('添加失败截图...'):
                allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # 封装简单assert_equal断言
    @classmethod
    def assert_equal(cls, A, B, log_info=None):
        try:
            Page.page_is_loading()
            assert A == B
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'{A} == {B}', extra=m)
            if log_info:
                cls.logger.info(f'{log_info}', extra=m)
        except Exception as e:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'{A} !== {B},错误原因：{e}', extra=m)
            with allure.step('添加失败截图...'):
                allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # 拖拽元素A到元素B位置
    @classmethod
    def drag_move_to_element(cls, ela_module, ela_page, ela_element, elb_module, elb_page, elb_element, log_info=None):
        try:
            Page.page_is_loading()
            ela = cls.element(ela_module, ela_page, ela_element)
            elb = cls.element(elb_module, elb_page, elb_element)
            action = ActionChains(cls.driver)
            action.drag_and_drop(ela, elb)
            action.perform()
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'移动成功--{ela_element}移动到{elb_element}', extra=m)
            if log_info:
                cls.logger.info(f'{log_info}', extra=m)
        except Exception as e:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'{ela_element}移动到{elb_element}失败,错误原因：{e}', extra=m)
            with allure.step('添加失败截图...'):
                allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # @classmethod
    # def wait_not_text(cls, module, page, element, not_expect,log_info=None):
    # s = traceback.extract_stack()
    # m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    # sleep(1)
    # try:
    #     WebDriverWait(cls.driver, 120).until_not(cls.element_text(module, page, element)==not_expect)
    #     cls.logger.info(f'{element}:状态[{not_expect}]发生变化', extra=m)
    #     if log_info:
    #         cls.logger.info(f'{log_info}', extra=m)
    # except Exception as e:
    #     cls.logger.info(f'{not_expect}：第120秒后仍然未改变,失败原因:{e}', extra=m)
    #     with allure.step('添加失败截图...'):
    #         allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # 状态从创建中变更为开发中或者失败
    @classmethod
    def wait_not_text(cls, module, page, element, log_info=None):
        try:
            i = 0
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            Page.page_is_loading()
            state_text = cls.element_text(module, page, element)
            while i < 120:
                sleep(1)
                if state_text == "开发中" or state_text == "失败":
                    cls.logger.info(f':{element}状态为:{state_text}', extra=m)
                    break
                i = i + 1
                state_text = cls.element_text(module, page, element)
                cls.logger.info(f'{element}状态为:{state_text}', extra=m)
            if log_info:
                cls.logger.info(f'{log_info}', extra=m)
            return str(state_text)
        except Exception as e:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'{element}状态120S后依然不是[开发中]或者[失败],错误原因：{e}', extra=m)
            with allure.step('添加失败截图...'):
                allure.attach(Page.driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    # 滑动至元素可见
    @classmethod
    def swipe_to_ele(cls, module, page, element, is_els=False, num=None):
        if not is_els:
            ele = cls.element(module, page, element)
            cls.driver.execute_script("arguments[0].scrollIntoView();", ele)
        if is_els:
            ele = cls.elements(module, page, element)[num]
            cls.driver.execute_script("arguments[0].scrollIntoView();", ele)

    # 判断是否有dialog弹窗
    @classmethod
    def check_dialog(cls):
        result = cls.driver.execute_script('return document.activeElement.innerText')
        return result.find("提示") >= 0 and result.find("确定") >= 0

    # page down按键
    @classmethod
    def page_down(cls):
        js = 'window.scroll(0,document.body.scrollHeight)'
        cls.driver.execute_script(js)
        # win32api.keybd_event(8, 0, 0, 0)
        # win32api.keybd_event(8, 0, win32con.KEYEVENTF_KEYUP, 0)

    # div内的滚动条
    @classmethod
    def div_bottom(cls, msg):
        js = 'document.getElementById("%s").scrollTop=5000000000' % msg
        cls.driver.execute_script(js)

    # 滚动到浏览器顶部
    @classmethod
    def js_top(cls):
        js_top = "var q=document.documentElement.scrollTop=0"
        cls.driver.execute_script(js_top)

    # 滚动到浏览器底部
    @classmethod
    def js_bottom(cls):
        js_bottom = "var q=document.documentElement.scrollTop=1000000"
        cls.driver.execute_script(js_bottom)

    @classmethod
    def js_click(cls, module, page, element):
        ele = cls.element(module, page, element)
        cls.driver.execute_script("arguments[0].click();", ele)

    # 下拉控件输入
    @classmethod
    def select_list(cls, encode, valve):
        js = "window.testvue." + encode + "='" + valve + "'"
        s = traceback.extract_stack()
        m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
        cls.logger.info(f'{js}', extra=m)
        sleep(1)
        cls.driver.execute_script(js)
        # 下拉控件输入

    @classmethod
    def select_location(cls, encode, valve):
        js = "window.testvue." + encode + ".push('" + valve + "')"
        sleep(1)
        cls.driver.execute_script(js)

    # 下拉控件清空
    @classmethod
    def clear_select(cls, encode):
        shift = "window.testvue." + encode + ".shift()"
        sleep(1)
        cls.driver.execute_script(shift)

    # 输入框清空
    @classmethod
    def clear_edit(cls, encode):
        shift = "window.testvue." + encode + "=''"
        sleep(1)
        cls.driver.execute_script(shift)

    @classmethod
    def el_js_click(cls, ele):
        cls.driver.execute_script("arguments[0].click();", ele)

    # # 切换至弹窗
    # @classmethod
    # def switch_alert(cls):
    #     WebDriverWait(cls.driver, 10).until(EC.alert_is_present())
    #     a = cls.driver.switch_to.alert()
    #     a.accept()
    #     s = traceback.extract_stack()
    #     m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    #     cls.logger.info('切换至弹窗OK', extra=m)

    # # 操作滑动控件
    # @classmethod
    # def control(cls, module, page, element, x=0, y=100):
    #     action_chains = ActionChains(cls.driver)
    #     el = cls.element(module, page, element)
    #     action_chains.click_and_hold(el).perform()
    #     action_chains.move_to_element_with_offset(el, x, y).perform()  # 滑动距离x横坐标，y纵坐标
    #     action_chains.release()

    # # 滑动至页面底部（end按键）
    # @classmethod
    # def swipe_down(cls):
    #     win32api.keybd_event(35, 0, 0, 0)
    #     win32api.keybd_event(35, 0, win32con.KEYEVENTF_KEYUP, 0)

    # @classmethod
    # def switch_windows(cls, title):
    #     handles = cls.driver.window_handles
    #     try:
    #         for handle in handles:
    #             if handle != cls.driver.current_window_handle:
    #                 cls.driver.switch_to.window(handle)
    #                 if title in cls.driver.title:
    #                     break
    #                 break
    #             break
    #     except Exception as e:
    #         s = traceback.extract_stack()
    #         m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    #         cls.logger.info(f'{e}', extra=m)

    # # iframe对象
    # @classmethod
    # def switch_iframe(cls, module, page, iframe):
    #     """
    #     :param module:
    #     :param page:
    #     :param iframe:
    #     :return:
    #     """
    #     # 加入隐性等待
    #     # 此处便可以传入定位参数
    #     iframe = WebDriverWait(cls.driver, 10).until(EC.frame_to_be_available_and_switch_to_it(module[page][iframe]))
    #     # 加入日志
    #     s = traceback.extract_stack()
    #     m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    #     cls.logger.info('切换iframe' + iframe + 'OK', extra=m)
    #     return iframe

    # # 切回主页面
    # @classmethod
    # def switch_defaultframe(cls):
    #     cls.driver.switch_to.default_content()
    #     # 加入日志
    #     s = traceback.extract_stack()
    #     m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    #     cls.logger.info('切回主页面OK', extra=m)

    # # 判断元素是否可点击
    # @classmethod
    # def click_enable(cls, module, page, element):
    #     el = cls.element(module, page, element)
    #     return el.is_enabled()

    # # 判断element是否消失
    # @classmethod
    # def element_is_display(cls, module, page, element):
    #     """
    #     当我们需要找到元素，无需判断该元素是否可见，只判断元素是否存在在页面中时用presence_of_element_located
    #     :param module:
    #     :param page:
    #     :param element:
    #     :return:
    #     """
    #     # 加入隐性等待
    #     # 此处便可以传入定位参数
    #     el = WebDriverWait(cls.driver, 10).until_not(EC.presence_of_element_located(module[page][element]))
    #     # 加入日志
    #     s = traceback.extract_stack()
    #     m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
    #     cls.logger.info('页面元素' + element + '消失', extra=m)
    #     return el

    # 根据日期创建目录，并且根据时间命令图片
    @classmethod
    def save_screenshot_bug(cls):
        """
        主动截图的方法，想要看到具体某个环节的照片
        :return: 主动截图的照片
        """
        # 生成年月日时分秒时间
        picture_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
        # 生成年月日
        directory_time = time.strftime("%Y-%m-%d", time.localtime(time.time()))
        # 获取到当前文件的目录，并检查是否有 directory_time 文件夹，如果不存在则自动新建 directory_time 文件
        try:
            File_Path = os.getcwd() + '\\' + "python_bug" + '\\' + directory_time + '\\'
            if not os.path.exists(File_Path):
                os.makedirs(File_Path)
                s = traceback.extract_stack()
                m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
                cls.logger.info(f'新建目录成功--{File_Path}', extra=m)
        except BaseException as msg:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'新建目录失败--{msg}', extra=m)

        try:

            # 下面这一段是用二进制写入的方法截图，希望比save_screenshot截图速度快一点，尽量避免截空白图
            url = os.getcwd() + '\\' + 'python_bug' + '\\' + directory_time + '\\' + picture_time + '.png'
            photo = cls.driver.get_screenshot_as_png()
            with open(url, 'wb') as f:
                f.write(photo)
            """
            # 下面这段是save_screenshot截图方法
            url = '.\\' + "python_bug" + '\\' + directory_time + '\\' + picture_time+'.png'
            cls.driver.save_screenshot(url)
            """
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'截图成功--{url}', extra=m)
        except BaseException as pic_msg:
            s = traceback.extract_stack()
            m = {'appname': s[-2].filename.split('\\')[-1] + f' line in {s[-2].lineno}'}
            cls.logger.info(f'截图失败--{pic_msg}', extra=m)


if __name__ == "__main__":
    pass
